//cheap mmc mp3 2006
#include <16f88.H>
#use delay(clock=8000000)
#use fast_io(a)
#use fast_io(b)
#use rs232(baud=9600, xmit=PIN_a7,rcv=PIN_b5)

#byte port_a 	=	0x05	//port a
#byte port_b		=	0x06	//b
#byte tris_a   	=	0x85	//port a
#byte tris_b		=	0x86	//b
#byte option_reg =	0x81
#byte  ADRESL	=	0x9E
#byte  ADRESH	=	0x1E
#byte ADCON0 	=	0x1F
#bit    GODONE	=	ADCON0.2
#byte ADCON1	=	0x9F
#byte ANSEL 	= 	0x9B
#byte OSCCON	=	0x8F
#byte CVRCON	=	0x9D
#byte CMCON	=	0x9C
#byte SSPCON	=	0x14
#byte SSPSTAT	=	0x94
#byte SSPBUF	=	0x13
#byte intcon  = 0X0B

#bit	 SSPEN	=	SSPCON.5
#bit SS			= port_a.0
#bit XCS		= port_a.1
#bit XDCS		=port_a.3
#bit XCLK		= port_b.4
#bit XDI		= port_b.1
#bit XDREQ		= port_a.4
#bit MCLR		= port_a.5
#bit XRESET 	= port_a.2
#bit XDO	 	= port_a.6
#bit MOSI		=port_b.2
#bit XDI_TRIS	=tris_b.1

#bit TRACKUP	= port_b.6
#bit TRACKDOWN		= port_b.7
#bit VOLUMEUP		= port_b.5
#bit VOLUMEDOWN		= port_b.3


#bit PAUSE		= port_b.0


///////////////////prototypes

void  find_bpb(void);
void  mmc_skip(char count);
void mmc_read(void);
char  getsongstart(int32 filenumber);
char  song(int32 startsector,int32 endsector);
void printout_rootdir(void);
void find_highest_song_number(void);
int32 get_next_shuffle(void);
char rand(void);
//super fat functions::::
int32 readfat(int32 fatoffset);




int mmc_init();
int mmc_response(unsigned char response);
int mmc_read_block_to_serial(int32 block_number);
int mmc_get_status();
char mmc_open_block(int32 block_number);
char mmc_get_byte(void);
void mmc_close_block(void);
void mmc_cancel_block(void);
int mmc_read_block_to_vs1011(int32 block_number);
#inline
void init(void);
void resetvs1011_hard(void);
void resetvs1011_soft(void);
void morezeroes(void);
int16 vs_command(char inout,address,a,b);
char vs_spi_write(char aa);
void play_range(int32 start, int32 end);
///////////////////////end prototypes

/////////////////////globals
char volume;
char data_lo, data_hi;

char BPB_SecPerClus;
int16 bpbstart;
int16 RootDirSectors;
int16 FirstDataSector;
int16 BPB_ResvdSecCnt;
int16 BPB_BytsPerSec;
int32 FirstRootDirSecNum; //;, songstarter, songlength, currentsong;

int32 songstarter;
int32 songlength;
int32 currentsong;
int32 fatstart, datsec;
int32 ccl, pcl, root_ccl;
int32 root_p_cl;
int32 highestsong;
int32 BPB_RootEntCnt;
int32 BPB_FATSz16;
int32 BPB_FATSz32;
int32 BPB_RootClus;
char BPB_NumFATs, fatmode;

char offset;
char column_pointer[16];
//char pointerh, pointerl, globalpointer;
char random_byte;//global variable for the random number generator (keeps the last value around for seeding purposes?)

////////////////////end globals
//BYTE CONST RNDL [256]= {208,75,137,110,218,247,165,23,248,216,204,59,118,35,193,29,83,158,93,57,224,194,3,207,205,149,203,22,129,171,10,
#rom  0x2100={208,75,137,110,218,247,165,23,248,216,204,59,118,35,193,29,83,158,93,57,224,194,3,207,205,149,203,22,129,171,10,
142,251,99,249,244,144,16,17,238,37,197,111,151,4,192,104,7,95,150,67,101,69,15,132,186,87,156,206,27,166,20,182, 
163,92,198,119,213,180,61,152,39,185,19,58,45,33,64,24,160,40,222,172,131,227,221,105,108,177,183,73,43,157,12,199,
113,9,245,126,130,106,120,210,215,31,167,246,76,139,195,239,50,229,169,55,122,212,30,112,6,85,141,82,236,21,32,89,
219,173,188,127,74,214,187,190,176,62,123,189,121,184,148,225,34,1,196,28,49,46,79,77,54,117,240,175,242,140,231,
13,128,25,97,146,228,179,116,70,53,178,18,254,78,109,155,241,233,81,255,159,202,102,2,174,14,147,234,60,8,84,52,98,
100,230,145,211,125,11,181,164,26,90,134,161,36,71,223,143,232,136,63,68,51,96,243,66,65,235,47,154,237,252,72,209,
114,138,115,86,250,5,94,133,42,44,88,226,103,253,124,168,91,48,217,56,80,135,220,107,191,201,162,153,38,170,41,200,0}


////////////////////////////////////////////////////////////////////////////////////////////////
#int_timer0
void volume_handler(void){
	if (!VOLUMEUP){
//		volume = spicommand(0x03,0x0b,0x00,0x00);
		if (volume<255){
			volume++;
		}
//		spicommand(0x02,0x0b,volume,volume);
	}
	if (!VOLUMEDOWN){
//		volume = spicommand(0x03,0x0b,0x00,0x00);
		if (volume>0){
			volume--;
		}
//		spicommand(0x02,0x0b,volume,volume);
	}
}


void main(void){
char flag, result, n, shufflemode;
	init();
	enable_interrupts(GLOBAL);
	enable_interrupts(INT_TIMER0);
	xcs=1;
	xdcs=1;
//	while(1){
		volume=0x23;
//		delay_ms(250);
//		printf("IKF");//putc(13); putc(10);
		result=mmc_init();
//		if (result==1){printf("nogo"); while(1){;}}
		if (result==1){while(1){;}}
//		BPB_BytsPerSec=0;
		find_bpb();
		resetvs1011_hard();
//		if(BPB_BytsPerSec!=0x200){
	//		printf("POP");	putc(13);putc(10);
//			find_bpb();
//		}
//	printf("card parameters found");	putc(13);putc(10);
	currentsong=0x01;
	find_highest_song_number();
	root_ccl= BPB_RootClus;
	flag='s';
	result=0x00;
//	shufflemode=0;
	if (!TRACKUP){
		for(n=0;n<16;n++){
			column_pointer[n]=0;		
		}
		while(!TRACKUP){random_byte++; offset++;}
		while(1){
			if ((TRACKUP)&&(TRACKDOWN)){
				currentsong=get_next_shuffle();
				result=getsongstart(currentsong);
				if(result==0x01){
//					printf("playing: %lX ",currentsong);	putc(13);putc(10);	putc(13);putc(10);
					flag=song( songstarter,(songstarter+(songlength/512)) );
				}
			}
		}
	}
	while(1){
////////////////////////////////////////////////////////auto track increment. press and hold...
//single increment/decrement of track is within the "song" function
		if (!TRACKUP){
			for(n=120;n>0;n--){
				delay_ms(3);
				if (TRACKUP){
					break;
				}
			}
			while (!TRACKUP){
				delay_ms(1);
				n++;
				if (n==15){
					n=0;
					currentsong++;
					if (currentsong>highestsong){currentsong=0x01;}//putc('s');}
					//putc('f');
				}
			}
		}
		if (!TRACKDOWN){
			for(n=120;n>0;n--){
				delay_ms(3);
				if (TRACKDOWN){
					break;
				}
			}
			while (!TRACKDOWN){
				delay_ms(1);
				n++;
				if (n==15){
					n=0;
					currentsong--;
					if (currentsong==0x00){currentsong=highestsong;}// putc('s');}
					//putc('f');
				}
			}
		}

//////////////////////////end AUTO TRACK INCREMENT	


		if ((TRACKUP)&&(TRACKDOWN)){
			n=0;
			result=getsongstart(currentsong);
			if(result==0x02){
				currentsong=0x01;
				flag='s';
//				printf("got to end of card, going back to track one");	putc(13);putc(10);
			}
			else if(result==0x01){
//				printf("playing: %lX ",currentsong);	putc(13);putc(10);
				flag=song( songstarter,(songstarter+(songlength/512)) );
			}
			if ((flag=='n')||(flag=='s')){
					currentsong++;
	//				printf("auto advanced");	putc(13);putc(10);
			}
			else if (flag=='p'){
				if (currentsong>1){
					currentsong--;
	//				printf("previous track attempt");	putc(13);putc(10);
				}
				else{currentsong=highestsong;}//loop around to the last track on the card
			}
		}
	}
}

char	getsongstart(int32 filenumber){
	char Emm,Pee,Three;
	int32 Largeccl, ccltemp, eocmark;
	int16  x, y, z;
//				printf("getting song paramaters for: %lX ",filenumber);
		eocmark=0;
//		if (fatmode==16){
//			mmc_open_block((Firstrootdirsecnum+(filenumber/16))+bpbstart);
//		}else{// if 32 mode:
			root_ccl= BPB_RootClus;
			y=filenumber/16;
			z=y;
			y=y/BPB_SecPerClus;
			x=y*BPB_SecPerClus;
			x=z-x;	
			while(y>0){
				root_p_cl=root_ccl;
				root_ccl=readfat(root_p_cl);
//				printf("current root cluster:%4LX   ",root_ccl);
				eocmark=root_ccl & 0x0FFFFFFF;
				y--;
			}
			if (eocmark>0x0fffffef){
				return (0x02);
			}
			ccltemp=root_ccl-2;
			ccltemp=ccltemp * (int32)BPB_SecPerClus;
			ccltemp=ccltemp + (int32)datsec;
			ccltemp=ccltemp + (int32)x;
			mmc_open_block(ccltemp); 
//		}
	mmc_skip((filenumber & 0x000F)*16);
	mmc_read();
//	printf("firstCharOf File Name is:%X   ",data_lo);
	if(data_lo==0x00){
		mmc_cancel_block();
		return(0x00);//was 0x02
	}
	if(data_lo != 0xe5){
//		putc(254);putc(1);
//		putc(data_lo);putc(data_hi);
//		mmc_skip(6);

//		mmc_read();
//		putc(data_lo);putc(data_hi);
//		mmc_read();
//		putc(data_lo);putc(data_hi);
//		mmc_read();
//		putc(data_lo);putc(data_hi);
//		putc('.');


		mmc_skip(3);
		mmc_read();
//		putc(data_lo);putc(data_hi);
		Emm=data_lo;
		Pee=data_hi;
		mmc_read();
//		putc(data_lo);putc(data_hi);	putc(13);putc(10);
		Three=data_lo;
		if (((Emm=='m') || (Emm=='M')|| (Emm=='W')|| (Emm=='w')) && ((Pee=='P') || (Pee=='p')|| (Pee=='A')|| (Pee=='a')) && (Three=='3')||(Three=='V')||(Three=='v')){
			mmc_skip(4);
			mmc_read();
			if (fatmode==32){
				Largeccl= ((int16)data_hi<<8)+data_lo;// clusterf high bytes. they're in a funny place to maintain compatibility with fat16
				Largeccl=Largeccl<<16;
			}
			mmc_skip(2);
			mmc_read();
			ccl= ((int32)data_hi<<8)+data_lo;
			if (fatmode==32){
				ccl = ccl+ Largeccl;   //add on the high bytes of the cluster address
			}
			songstarter=( ((((int32)data_lo+((int32)data_hi<<8))-0)  *BPB_SecPerClus) +bpbstart+Firstdatasector);
			mmc_read();
			*(((char*)&songlength)+0)=data_lo;
 			*(((char*)&songlength)+1)=data_hi;
								//songlength=(int16)data_lo + ((int16)data_hi<<8);
			mmc_read();
			*(((char*)&songlength)+2)=data_lo;
			*(((char*)&songlength)+3)=data_hi;
								//songlength=songlength + (((int16)data_lo+((int16)data_hi<<8))  <<16);
			mmc_cancel_block();
			return(0x01);
		}
	}
	mmc_cancel_block();
	return(0x00);
}

char	song(int32 startsector,int32 endsector){
	int32 ccltemp;
	short int hardflag;
	char insanity;//, sectprint;
	char shutdowncounter, x ;
	x=0;
	insanity=0;
//	sectprint=1;
	resetvs1011_soft();
	startsector	=	startsector/BPB_SecPerClus;
	endsector	=	endsector/BPB_SecPerClus;
	for(;startsector<=endsector;startsector++){
		for (x=0;x<BPB_SecPerClus;x++){
			ccltemp=ccl-2;
			ccltemp=ccltemp * (int32)BPB_SecPerClus;
			ccltemp=ccltemp + (int32)datsec;
			ccltemp=ccltemp + (int32)x;
		//	if (sectprint==1){
		//		printout_sector(ccltemp); 
		//		sectprint=0;
		//	}
			insanity = mmc_read_block_to_vs1011(ccltemp);//this is where it all gets played
			if(insanity==1){insanity = mmc_read_block_to_vs1011(ccltemp);}//try again
			if(insanity==1){vs_command(0x02,0x0b,0xFE,0xFE);}//go silent and wait for user input

			if (!TRACKUP){return('n');}
			if (!TRACKDOWN){
				if (currentsong>0){return('p');}
			}
		}
		pcl=ccl;
		ccl=readfat(pcl);
		vs_command(0x02,0x0b,volume,volume);
		if(!PAUSE){
			delay_ms(20);
			while (!PAUSE){delay_ms(20);}
//			delay_ms(20);
			while (PAUSE){delay_ms(20);}
//			delay_ms(20);
			while (!PAUSE){;}
			delay_ms(20);
		}
	}
	return('s');
}




////////////////////////////////////////////////////////////////////////////////////////////////
void play_range(int32 start, int32 end){
	for(;start<end;start++){
		mmc_read_block_to_vs1011(start);
	}
}


void mmc_cancel_block(void){
//	SS=1;
//	delay_cycles(10);
//	SS=0;
	SPI_WRITE(0x4C);                // send mmc read single block command
	SPI_WRITE(0); // arguments are address
	SPI_WRITE(0);
	SPI_WRITE(0);
	SPI_WRITE(0x00);
//	mmc_response(0x00);
//	mmc_response(0xFE);
//	for (i=0;i<8;i++){
//		SPI_WRITE(0xFF); 
//	}
		SPI_WRITE(0xFF); 		SPI_WRITE(0xFF); 
	SS=1;
//	for (i=0;i<4;i++){
//		SPI_WRITE(0xFF); 
//	}	
		SPI_WRITE(0xFF); 
}

///////////// ///////////// ////////////////// ///////////????????????//////////////
void find_bpb(){
int32 bigtemp;
	bpbstart=0;
	mmc_open_block(0);
	mmc_skip(0xE3);
	mmc_read();
	bpbstart=((int16) data_hi<<8);
	bpbstart+=data_lo;
	mmc_cancel_block();
//	mmc_skip(0x1C);
//	mmc_close_block();
	mmc_open_block((int32) bpbstart);

	mmc_skip(5);
	mmc_read();
	BPB_bytspersec=data_hi;	//11
	mmc_read();
	BPB_bytspersec=BPB_bytspersec+((int16) data_lo<<8); //11.12
	BPB_SecPerClus=data_hi;	//13
	mmc_read();
	BPB_ResvdSecCnt=((int16) data_hi<<8)+data_lo;  //14.15
	mmc_read();

	BPB_NumFATs=data_lo;  //16
	BPB_RootEntCnt=data_hi;  //17
	mmc_read();
	BPB_RootEntCnt=BPB_RootEntCnt+((int16) data_lo<<8);  //17.18
	mmc_read();
	mmc_read();
	BPB_FATSz16=data_lo+((int16) data_hi<<8);  //22.23

	mmc_skip(6);
	mmc_read();
	BPB_FATSz32=0;
	BPB_FATSz32=data_lo+((int32) data_hi<<8);  //36.37
	bigtemp=BPB_FATSz32;
	mmc_read();
	BPB_FATSz32=data_lo+((int32) data_hi<<8);  //36.37.38.39
	BPB_FATSz32=BPB_FATSz32 <<16;
	BPB_FATSz32=bigtemp+BPB_FATSz32;
	mmc_read();
	mmc_read();
	mmc_read();
	BPB_RootClus=data_lo+((int32) data_hi<<8);  //44.45
	bigtemp=BPB_RootClus;

	mmc_read();
//	mmc_skip(0xE8);
//	mmc_close_block();
	mmc_cancel_block();

	BPB_RootClus=data_lo+((int32) data_hi<<8);  //44.45.46.47
	BPB_RootClus=BPB_RootClus<<16;
	BPB_RootClus=BPB_RootClus+bigtemp;
	root_ccl=BPB_RootClus;

	fatstart=bpbstart+BPB_ResvdSecCnt;

	RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec;
	if(BPB_FATSz16 != 0){
		fatmode=16;
		FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16) + RootDirSectors;
	}else{
 		fatmode=32;
		FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz32) + RootDirSectors;
	}
	FirstRootDirSecNum = BPB_ResvdSecCnt + ((int32) BPB_NumFATs * (int32) BPB_FATSz16);
		datsec = (int32)bpbstart + (int32)firstdatasector;




//	printf("fm: %4D",fatmode);			putc(13);putc(10);
//	printf(" 16: %4LX",BPB_FATSz16); 	putc(13);putc(10);
//	printf(" clst: %4LX",BPB_RootClus);	putc(13);putc(10);
//	printf(" bpb %4LX",bpbstart);			putc(13);putc(10);
	



//	putc(13);putc(10); printf(" bpbst:%4LX  ",bpbstart);
//	putc(13);putc(10); printf(" BPB_bpsec:%4LX  ",BPB_bytspersec);
//	putc(13);putc(10); printf(" BPB_ScPeC:%4LX  ",BPB_SecPerClus);
//	putc(13);putc(10); printf(" BPB_RsvdScCt:%4LX  ",BPB_ResvdSecCnt);	  
//	putc(13);putc(10); printf(" BPB_NmFAT:%4LX  ",BPB_NumFATs);	
//	putc(13);putc(10); printf(" BPB_RootEntCnt:%4LX  ",BPB_RootEntCnt);
//	putc(13);putc(10); printf(" BPB_FATSz16:%4LX  ",BPB_FATSz16);
//	putc(13);putc(10); printf(" fatstart: %4LX  ",fatstart);
//	putc(13);putc(10); printf(" FirstRootDirSecNum:%4LX  ",FirstRootDirSecNum);
//	putc(13);putc(10); printf(" FirstDataSector:%4LX  ",FirstDataSector);
//	putc(13);putc(10); printf(" RootDirSectors:%4LX  ",RootDirSectors);
//	putc(13);putc(10);

}
////////////?????????????????/////////////////////?///////////////////////




void find_highest_song_number(void){
int32 clust_high;
int32 eocmark;
	root_ccl= BPB_RootClus;
	clust_high=0;
	do{
		root_p_cl=root_ccl;
		root_ccl=readfat(root_p_cl);
//		printf("cluster seek:%4LX   ",root_ccl);
		eocmark=root_ccl & 0x0FFFFFFF;
		clust_high++;
	//	putc('Z');
	}while(eocmark<0x0fffffef);
	highestsong=clust_high * BPB_SecPerClus;
	highestsong=	highestsong*16;
	highestsong--;
//	printf("clust_high:: %LX ",clust_high);	putc(13);putc(10);
//	printf("highestsong:: %LX ",highestsong);	putc(13);putc(10);
	
}



//-----------------------------------//this is limited to 2048 directiry entries
int32 get_next_shuffle(void){
int16 a, work;
char b, c;
	work=0xffff;
//	printf("seeking number----------:");
//	putc(13);putc(10);
	while(work>highestsong){
		b = rand();
		b = b/16;
//		printf("column number: %X ",b);
		//putc(13);putc(10);
//		printf("is up to: %X ",column_pointer[b]);
//		putc(13);putc(10);
		if (column_pointer[b]<255){
			c = column_pointer[b]++;
//			printf(" c is: %X ",c);
			c=c+offset;

			work=read_eeprom(c);
//			work=RNDL[c];
//			printf("work is: %lX ",work);
			a = (int16) b * 256;
			work=work+a;
//			printf("for a final number: %lX ",work);
		}
	}
//	printf("random hopeful!: %lX ",work);
	return (work);
}



char rand(void)	{
	char sum;
	sum = 0;
	// This calculates parity on the selected bits (mask = 0xb4).
	if(random_byte & 0x80){ sum   = 1;}
	if(random_byte & 0x20){ sum ^= 1;}
	if(random_byte & 0x10){ sum ^= 1;}
	if(random_byte & 0x04){ sum ^= 1;}
	random_byte <<= 1;
	random_byte |= sum;
	return(random_byte);
	
}








//super fat :::::
//-----------------------------------
////////////////////////////
int32 readfat(int32 fatoffset){
	int16 temp;
	int32 tempb;
	int8	los;
	temp=0;
//	if (fatmode==32){
		fatoffset=fatoffset*2;
//	}
	los = *(((char*)&fatoffset)+0);	//the bottom byte of the address goes directly to a word in the FAT
	fatoffset=fatoffset / 256; 
	fatoffset+=fatstart;
	mmc_open_block(fatoffset);
	mmc_skip(los);
	mmc_read();
	temp = ((int16) data_hi * 256)+ (int16) data_lo;
//	if (fatmode==16){
//		return temp;//only two bytes per entry
//	}
	mmc_read();//for fat32, four bytes per entry
	tempb=0;
	tempb = ((int16) data_hi * 256)+ (int16) data_lo;
	tempb=tempb<<16;
	tempb=tempb+(int32) temp;
	mmc_cancel_block();
//	printf(" cluster getter returns:::%4LX   ",tempb);
	return tempb;
}
//-----------------------------------

////////////////////////////////////////////////////////////////////////////////////////////////
void resetvs1011_hard(void){
	SS=1;	xcs=1;	xdcs=1;
	delay_ms(500);
	do{
		xreset=0;//hard reset
		delay_cycles(100);
		xreset=1;
		delay_cycles(500);
		vs_command(0x02,0x00,0x00,0x04);//sets new mode w/o shared pin and reset bit
		delay_ms(100);
	}while(!XDREQ);
	delay_ms(1);
	vs_command(0x02,0x00,0x08,0x00);//go into new mode (w/o pin sharing, w/o sdi tests)
	delay_ms(1);
	vs_command(0x02,0x0b,volume,volume);//set volume
	delay_ms(1);
	vs_command(0x02,0x03,0x99,0x50);// set xtal 12.960MHZ + doubler
//	printf("_%x",vs_command(0x03,0x03,0x99,0x50));
}


//-----------------------------------
void resetvs1011_soft(void){
	vs_command(0x02,0x00,0x08,0x04);//go into new mode (w/o pin sharing, w/o sdi tests)
	delay_ms(1);
	vs_command(0x02,0x0b,volume,volume);//set volume
	delay_ms(1);
	vs_command(0x02,0x03,0x99,0x50);// set xtal 12.960MHZ + doubler
	delay_ms(1);
//	morezeroes();
}


/*///////////////////////////////////////////////////////////////////////////////////////////////
void morezeroes(void){
int16 c;
	xcs=1;
	if (XDREQ){
		xdcs=0;
		for(c=0;c<2048;c++){
			vs_spi_write(0);
			while (!XDREQ){;}
		}
		xdcs=1;
	}
}


*////////////////////////////////////////////////////////////////////////////////////////////////
char vs_spi_write(char aa){
char h;
char i;
	h=0;
	i=7;
	XCLK=0;delay_cycles(10);
	do{ 
		XDI=bit_test(aa,i);	
		XCLK=1;
		if(XDO==1){bit_set(h,i);}	
		XCLK=0;

	}while((i--)>0);
	return (h);
}


////////////////////////////////////////////////////////////////////////////////////////////////
int16 vs_command(char inout, address,a,b){//for sending non-song related data to the decoder..
int16 result;
	SS=1;
	xdcs=1;
	SSPEN=0;
	XDI_TRIS=0;
	xcs=0;
	vs_spi_write(inout); 
	vs_spi_write(address);//load the buffer with address
	*(((char*)&result)+1)=vs_spi_write(a);			//load buffer with hi  data(ignored on read command)
	*(((char*)&result)+0)=vs_spi_write(b);			//load buffer with data low value
	xcs=1;				//deselect the vs1011 command registers (go into sdi mode)
	XDI_TRIS=1;
	SSPEN=1;
	return (result);
}


void mmc_read(void){
	data_lo=SPI_READ(0xFF);
	data_hi=SPI_READ(0xFF);
}


/////////////////////////////////////////////////////////////////////////////////////////////////
void mmc_skip(char count){
	for (;count>0;count--){
		SPI_WRITE(0xFF);
		SPI_WRITE(0xFF);
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////
char mmc_open_block(int32 block_number){
	block_number*=512;
	SS=0;                     // set SS = 0 (on)
	SPI_WRITE(0x51);                // send mmc read single block command
	SPI_WRITE(*(((char*)&block_number)+3)); // arguments are address
	SPI_WRITE(*(((char*)&block_number)+2));
	SPI_WRITE(*(((char*)&block_number)+1));
	SPI_WRITE(0x00);
	SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
	if((mmc_response(0x00))==1) return 1;   // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative)
	if((mmc_response(0xFE))==1) return 1;   // wait for data token
	return(0);
}
void mmc_close_block(void){
	SPI_READ(0xFF);                 // CRC bytes that are not needed
	SPI_READ(0xFF);
	SS=1;            // set SS = 1 (off)
	SPI_WRITE(0xFF);                // give mmc the clocks it needs to finish off
}

////////////////////////////////////////////////////////////////////////////////////////////////////
int mmc_init(){	//Initialises the MMC into SPI mode and sets block size
char p;
int i;
	SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED);
	SSPSTAT |= 0x40;                          // set CKE = 1 - clock idle low
	SSPCON &= 0xEF;                          // set CKP = 0 - data valid on rising edge
	SS=1;			//                    // set SS = 1 (off)
	for(i=0;i<10;i++){                       // initialise the MMC card into SPI mode by sending clks on
	        SPI_WRITE(0xFF);
	}
	SS=0;				                     // set SS = 0 (on) tells card to go to spi mode when it receives reset
	SPI_WRITE(0x40);                        // send reset command
	for(p=4;p>0;p--){
		SPI_WRITE(0x00);                        // all the arguments are 0x00 for the reset command
	}
	SPI_WRITE(0x95);                        // precalculated checksum as we are still in MMC mode
//	puts("Sent go to SPI\n\r");
	if(mmc_response(0x01)==1) return 1;     // if = 1 then there was a timeout waiting for 0x01 from the mmc (bad!)
//	puts("Got response from MMC\n\r");
	i = 0;
	while((i < 255) && (mmc_response(0x00)==1)){     // must keep sending command if response
	        SPI_WRITE(0x41);                // send mmc command one to bring out of idle state
		for(p=4;p>0;p--){
	 	       SPI_WRITE(0x00);
		}
	        SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
	        i++;
	//	printf("%d ",i);
	}
	if(i >= 254) return 1;                   // if >= 254 then there was a timeout waiting for 0x00 from the mmc (bad!)
//	puts("Got out of idle response from MMC\n\r");
	SS=1;                    // set SS = 1 (off)
	SPI_WRITE(0xFF);                        // extra clocks to allow mmc to finish off what it is doing
	SS=0;                     // set SS = 0 (on)
	SPI_WRITE(0x50);                // send mmc command one to bring out of idle state
	SPI_WRITE(0x00);
	SPI_WRITE(0x00);
	SPI_WRITE(0x02);                // high block length bits - 512 bytes
	SPI_WRITE(0x00);                // low block length bits
	SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
	if((mmc_response(0x00))==1) return 1; //(bad!)
	SS=1;            // set SS = 1 (off)
//	puts("Got set block length response from MMC\n\r");
	return 0; //(good)
}


////////////////////////////////////////////////////////////////////////////////////////////////
int mmc_get_status(){	// Get the status register of the MMC, for debugging
char  p;
	xcs=1;
	xdcs=1;
	SS=0;                     // set SS = 0 (on)
	SPI_WRITE(0x58);                // send mmc command one
	for(p=4;p>0;p--){
		SPI_WRITE(0x00);
	}
	SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
	SS=1;                    // set SS = 1 (off)
	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////
int mmc_response(unsigned char response){	//reads the MMC until we get the response we want or timeout
	unsigned long count = 0x0FFF;           		// 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point
	while(SPI_READ(0xFF) != response && --count > 0);
	if(count==0) return 1;                  			// loop was exited due to timeout (bad)
	else return 0;                          			// loop was exited before timeout (good)
}

////////////////////////////////////////////////////////////////////////////////////////////////
int mmc_read_block_to_serial(int32 block_number){	//read block from the MMC and outputs each byte to RS232. just for fun
unsigned long i;
	mmc_open_block(block_number);
	for(i=0;i<512;i++){
			putc(SPI_READ(0xFF));               // we should now receive 512 bytes
	}
	mmc_close_block();
//	puts("\n\r----End of read block----\n\r");
	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////
int mmc_read_block_to_vs1011(int32 block_number){
unsigned long i, ii;
	SSPEN=1;
	block_number*=2;
	SS=0;                     // set SS = 0 (on)
	SPI_WRITE(0x51);                // send mmc read single block command
	SPI_WRITE(*(((char*)&block_number)+2)); // arguments are address
	SPI_WRITE(*(((char*)&block_number)+1));
	SPI_WRITE(*(((char*)&block_number)+0));
	SPI_WRITE(0x00);
	SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
	if((mmc_response(0x00))==1) return 1;   // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative)
	if((mmc_response(0xFE))==1) return 1;   // wait for data token
//	MOSI=1;
	SSPEN=0;//switch to manual control of SPI port pins. the pic will not be reading these bytes into sspbuf
	XdCS=0;//allow vs1011 to see the spi data
	for(i=16;i>0;i--){
		while(!XDREQ){;}
		for(ii=0;ii<32;ii++){
		
			XCLK=1;XCLK=0; //doing it this way (without looping) to kill off a few cycles per byte
			XCLK=1;XCLK=0;//if you really want to go crazy, do this whole thing 32 times
			XCLK=1;XCLK=0;// that would save you about 1000 clock cycles or so, per block
			XCLK=1;XCLK=0;
			XCLK=1;XCLK=0;
			XCLK=1;XCLK=0;
			XCLK=1;XCLK=0;
			XCLK=1;XCLK=0;
		}
	}
	XdCS=1;//turn off vs1011 chip select
	SSPEN=1;//back to normal spi mode for pic to mmc communication
	SPI_READ(0xFF);                 // CRC bytes that are not needed, so we just use 0xFF
	SPI_READ(0xFF);
	SS=1;            // set SS = 1 (off)
	SPI_WRITE(0xFF);// give mmc the clocks it needs to finish off
	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////
#inline
void init(void){
	tris_a=0b01110000;
	tris_b=0b11101011;
//	port_a=0;
//	XCS=1;
	option_reg	=0b00000111;
//	option_reg= 0b00000000;//allow rb pullups so that external pullup resistors are not needed 
//	ADCON0 	=	0b00000000;//not using the adc, but here are the control bytes
//	ADCON1	=	0b00000000;
//	ANSEL 		= 	0b00000000;
	OSCCON	=	0b01110010; // 01110010 for internal 8MHz clock, 0b01110000 otherwise
//	CVRCON	=	0b00000000; //00001101for 3.3 volt output in case you want it for some reason
//	CMCON	=	0b00000111;
}

